# 1） 总流程梳理，项目文档完善
# 2）in a valid position——偶发。
# 3）集卡任务设置失败（findroute()没成功），就让它回休息区，而不是现在一直在重复设置任务。这与突然来了下一帧集卡任务，其中该集卡有新任务，又被重置任务，统筹考虑

# 下一版本优化
# 1）停车后，车辆角度要校对下：0度，或90度。
# 2） 重构时，取消viewsetting

import os
import copy
import json
import time
from datetime import datetime, timedelta
import traci
import random
from loguru import logger
import subprocess
import shutil
import glob
import traci.constants as tc
import traceback

import netxml
import routepy


ADDED_RANGES = {}
ADDED_JUNCTIONS = set()
VESSEL2LANE = {}
TRUCK_SUBSCRIBED_SET = set()  #存储、维护路网中所有作业车辆，暂时不用。traci.vehicle.getIDList() = TRUCK_SUBSCRIBED_SET + TRUCK_UNSUBSCRIBED_SET
TRUCK_UNSUBSCRIBED_SET = set()  #存储、维护路网中不输出坐标等信息给Digital Twin(DT)、无作业任务的车辆，可以直接调用setRoute()
VEHICLE_TO_STOP_COORD = {}  #用于存储APPEND_STOP_LIST中随机选择的停车点信息
ADDED_UPDATED_VEHICLE_ID_EDGE = {}
WARNED_VEHICLE_SET = set()


def append_rest_stop_to_vehicle(ROUTE_FILE, vehicle_id, configs):
    """
    主要用于无vessel_id等属性的车辆从现有位置驶向休息区
    :param ROUTE_FILE: 
    :param vehicle_id: 
    :return: 
    """""
    global APPEND_STOP_LIST, VEHICLE_TO_STOP_COORD, TRUCK_SUBSCRIBED_SET,TRUCK_UNSUBSCRIBED_SET, ADDED_UPDATED_VEHICLE_ID_EDGE
    if vehicle_id not in ADDED_UPDATED_VEHICLE_ID_EDGE:
        current_edge = routepy.get_edge_from_rouxml(ROUTE_FILE, vehicle_id)
    elif vehicle_id in ADDED_UPDATED_VEHICLE_ID_EDGE:
        current_edge = ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id]
    else:
        current_edge = traci.vehicle.getRoadID(vehicle_id)
        if current_edge is None or current_edge == "":
            current_edge = random.choice(routepy.extract_route_edges_from_file(ROUTE_FILENAME))

    random_stop_temp = min(APPEND_STOP_LIST, key=lambda x: x['priority'])
    APPEND_STOP_LIST.remove(random_stop_temp)
    VEHICLE_TO_STOP_COORD[vehicle_id] = random_stop_temp
    rest_stop_edge = random_stop_temp['lane'][:-2]
    lane_index = 0
    lane_pos = 5
    lane_length = traci.lane.getLength(random_stop_temp['lane'])
    if lane_pos > lane_length:
        lane_pos = lane_length
    rest_route = traci.simulation.findRoute(current_edge, rest_stop_edge)
    traci.vehicle.setRoute(vehicle_id, rest_route.edges)
    traci.vehicle.setStop(vehID=vehicle_id, edgeID=rest_stop_edge, laneIndex=lane_index, pos=lane_pos, duration=configs.TRUCK_STOP_TIME)

    traci.vehicle.subscribe(vehicle_id, [
        tc.VAR_POSITION,
        tc.VAR_ANGLE,
        tc.VAR_SPEED,
        tc.VAR_ROAD_ID,
        tc.VAR_LANE_INDEX,
        tc.VAR_LANE_ID,
        tc.VAR_LANEPOSITION
    ])
    TRUCK_SUBSCRIBED_SET.add(vehicle_id)


def redis_task_data_manager(task_data, configs, vehicle_id=None):
    if task_data:
        if vehicle_id is not None:
            # configs.redis_conn.hset('tk_data', vehicle_id, json.dumps(task_data))
            logger.warning(f"集卡{vehicle_id}将驶向停车区，因为其vessel_id不存在！")
        if vehicle_id is None:
            # configs.redis_conn.hset("tk_data", mapping=task_data)
            logger.warning(f"标志3：向孪生发送{len(task_data)}辆车坐标信息。")


def vehicle_task_check(vehicle_id,task_info,simulation_time, configs):
    """
    主要用于清理无效（无vessel_id, 坐标为空）集卡作业数据：通过发送"stop_exist": 0告知redis中task_data管理者删除集卡vehicle_id的无效作业数据
    """""
    global VESSEL2LANE, ROUTE_FILENAME,TRUCK_UNSUBSCRIBED_SET
    check_vessel_id = task_info["vessel_id"]
    if check_vessel_id not in VESSEL2LANE.keys():
        append_rest_stop_to_vehicle(ROUTE_FILENAME, vehicle_id, configs, task_info, simulation_time)
        return False
    elif len(task_info["stop_coord"]) >= 1:
        return True


def set_vehicle_route_and_stop(vehicle_id, task_info, configs, current_edge_update = None, current_pos_update = None, current_edge_add=None, current_pos_add=None):
    global VESSEL2LANE, APPEND_STOP_LIST, ROUTE_FILENAME, VEHICLE_TO_STOP_COORD,TRUCK_UNSUBSCRIBED_SET, TRUCK_SUBSCRIBED_SET, ADDED_UPDATED_VEHICLE_ID_EDGE
    try:
        if vehicle_id not in ADDED_UPDATED_VEHICLE_ID_EDGE:
            current_edge = routepy.get_edge_from_rouxml(ROUTE_FILENAME, vehicle_id)
        elif vehicle_id in ADDED_UPDATED_VEHICLE_ID_EDGE:
            current_edge = ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id]
        else:
            current_edge = traci.vehicle.getRoadID(vehicle_id)
            if current_edge is None or current_edge == "":
                current_edge = "-r10-2Tr11-2"

        stop_coords = task_info["stop_coord"]
        route_edges = []
        temp_stop = min(APPEND_STOP_LIST, key=lambda x: x['priority'])
        APPEND_STOP_LIST.remove(temp_stop)
        VEHICLE_TO_STOP_COORD[vehicle_id] = temp_stop
        stop_coords.append(temp_stop)
        # 设置路由
        for stop in stop_coords:
            lane_type = stop.get("type")
            if lane_type == "yard":
                stop_edge = stop.get("lane")[:-2]
            elif lane_type == "qc":
                sumo_stop_lane_index = int(stop.get("lane")[-1])
                vessel_id = task_info["vessel_id"]
                stop_lane = VESSEL2LANE[vessel_id]['dtlane2sumolane'][sumo_stop_lane_index]
                stop_edge = stop_lane[:-2]
            elif lane_type == "rest":
                stop_edge = stop.get("lane")[:-2]
            route = traci.simulation.findRoute(current_edge, stop_edge)

            if route.edges:
                if len(route_edges) == 0:
                    route_edges.extend(list(route.edges))
                else:
                    route_edges.pop()
                    route_edges.extend(list(route.edges))
                current_edge = route.edges[-1]
            else:
                logger.info(f"集卡{vehicle_id}没有找到去 {stop_edge} 的路径，请检查路网是否正确。")
                # logger.error(traceback.format_exc())
                return False
        tuple_route_edges = tuple(route_edges)

        if route_edges:
            tuple_route_edges = tuple(route_edges)
            traci.vehicle.setRoute(vehicle_id, tuple_route_edges)

        traci.vehicle.setRoute(vehicle_id, tuple_route_edges)

        # 设置停车点
        direction = VESSEL2LANE[vessel_id]["direction"]
        for stop in stop_coords:
            stop_x, stop_y = stop["coord"]
            lane_type = stop.get("type")
            lane = stop["lane"]
            worktime = float(stop["worktime"])
            lane_index = int(lane[-1])
            lane_length = 0

            if lane_type == "qc":
                lane_temp = VESSEL2LANE[vessel_id]["dtlane2sumolane"][lane_index]
                stop_edge = lane_temp[:-2]
                lane_id = int(lane_temp[-1])
                lane_length = traci.lane.getLength(lane_temp)
                if direction == "right":
                    lane_pos = abs(stop_x - VESSEL2LANE[vessel_id]["start_x"])
                elif direction == "left":
                    lane_pos = abs(VESSEL2LANE[vessel_id]["start_x"] - stop_x)

            elif lane_type == "yard":
                stop_edge = lane[:-2]
                lane_id = lane_index
                lane_length = traci.lane.getLength(lane)
                if lane[0] == "-": # 负向
                    stop_edge = stop_edge[1:]
                    junction_id_from = stop_edge.split("T")[1]
                    x_from, y_from = traci.junction.getPosition(junction_id_from)
                    lane_pos = abs(x_from - stop_x)
                else:
                    junction_id_from = stop_edge.split("T")[0]
                    x_from, y_from = traci.junction.getPosition(junction_id_from)
                    lane_pos = abs(stop_x - x_from)

            elif lane_type == "rest":
                stop_edge = lane[:-2]
                lane_id = lane_index
                lane_length = traci.lane.getLength(lane)
                if lane[0] == "-":
                    stop_edge_temp = stop_edge[1:]
                    junction_id_from = stop_edge_temp.split("T")[0]
                    junction_id_to = stop_edge.split("T")[1]
                    x_from, y_from = traci.junction.getPosition(junction_id_from)
                    x_to, y_to = traci.junction.getPosition(junction_id_to)
                    if abs(y_from - y_to) < 1 and abs(x_from - x_to) > 1:
                        lane_pos = abs(stop_x - x_from)
                    elif abs(x_from - x_to) < 1 and abs(y_from - y_to) > 1:
                        lane_pos = abs(stop_y - y_from)
                else:
                    junction_id_from = stop_edge.split("T")[0]
                    junction_id_to = stop_edge.split("T")[1]
                    x_from, y_from = traci.junction.getPosition(junction_id_from)
                    x_to, y_to = traci.junction.getPosition(junction_id_to)
                    if abs(y_from - y_to) < 1 and abs(x_from - x_to) > 1:
                        lane_pos = abs(stop_x - x_from)
                    elif abs(x_from - x_to) < 1 and abs(y_from - y_to) > 1:
                        lane_pos = abs(stop_y - y_from)

            if lane_pos > lane_length:
                lane_pos = lane_length
            if current_edge_update is not None and current_pos_update is not None and current_edge_update == stop_edge and lane_pos < current_pos_update:
                lane_pos = current_pos_update + 0.5
                traci.vehicle.setStop(vehID=vehicle_id, edgeID=stop_edge, laneIndex=lane_id, pos=lane_pos, duration=worktime)
            elif current_edge_add is not None and current_pos_add is not None and current_edge_add == stop_edge and lane_pos < current_pos_add:
                lane_pos = current_pos_add + 0.5
                traci.vehicle.setStop(vehID=vehicle_id, edgeID=stop_edge, laneIndex=lane_id, pos=lane_pos, duration=worktime)
            else:
                traci.vehicle.setStop(vehID=vehicle_id, edgeID=stop_edge, laneIndex=lane_id, pos=lane_pos, duration=worktime)

        # 添加订阅
        traci.vehicle.subscribe(vehicle_id, [
            tc.VAR_POSITION,
            tc.VAR_ANGLE,
            tc.VAR_SPEED,
            tc.VAR_ROAD_ID,
            tc.VAR_LANE_INDEX,
            tc.VAR_LANE_ID,
            tc.VAR_LANEPOSITION
        ])
        TRUCK_SUBSCRIBED_SET.add(vehicle_id)
        if vehicle_id in TRUCK_UNSUBSCRIBED_SET:
            TRUCK_UNSUBSCRIBED_SET.remove(vehicle_id)

    except Exception as e:
        logger.error(f"设置车辆 {vehicle_id} 的任务时发生错误。\n其路径为：{route_edges} \n任务为：{task_info}')")
        return False


def restart_simulation(net_file,configs):
    try:
        logger.info("关闭当前仿真会话...")
        traci.close()
        logger.info(f"启动新的仿真会话，使用新路网文件: {net_file}")
        traci.start([configs.sumoBinary, "-c", "dt.sumocfg"])
    except Exception as e:
        logger.error(f"启动仿真时发生错误: {e}")


def clean_temp_files():
    for file in glob.glob("*.rou.xml"):
        if os.path.basename(file) == "type.rou.xml":
            continue
        os.remove(file)

    for file in glob.glob("net\\*.net.xml"):
        if len(os.path.basename(file)) > 16:
            os.remove(file)
    for file in glob.glob("net\\net_with_id\\*.net.xml"):
        if len(os.path.basename(file)) > 16:
            os.remove(file)

def initialize_netxml(vessel_data, configs):
    global ADDED_JUNCTIONS, ADDED_RANGES, VESSEL2LANE
    i = 0
    intermediate_files = []

    for vessel, vessel_info in vessel_data.items():
        vessel_id = vessel
        st_x = vessel_info.get("tail_x", None)
        ed_x = vessel_info.get("head_x", None)
        if i != 0:
            net_file_name = netxml.add_netxml(net_file_name, vessel_id, st_x, ed_x, ADDED_JUNCTIONS, ADDED_RANGES,
                                              VESSEL2LANE)
            intermediate_files.append(net_file_name)
        else:
            net_file_name = netxml.add_netxml(configs.BASE_NET_FILE, vessel_id, st_x, ed_x, ADDED_JUNCTIONS,
                                              ADDED_RANGES, VESSEL2LANE)
            intermediate_files.append(net_file_name)
        i += 1

    if not os.path.exists('net\\net_with_id'):
        os.makedirs('net\\net_with_id')

    for temp_file in intermediate_files[:-1]:
        if os.path.exists(temp_file):
            os.remove(temp_file)

    target_filename = os.path.basename(net_file_name).replace('.net.xml', '_id.net.xml')
    target_file_path = os.path.join('net\\net_with_id', target_filename)
    shutil.copy(net_file_name, target_file_path)

    return net_file_name, target_file_path


def update_netxml(NET_FILENAME, vessel_data, NET_FILENAME_ID):
    global ADDED_JUNCTIONS
    global ADDED_RANGES
    global VESSEL2LANE
    NET_FILENAME_TEMP = os.path.basename(NET_FILENAME)
    str1 = NET_FILENAME_TEMP.split('.')[0]
    str2 = str1[9:]
    old_vessel_list = str2.split('-')

    old_keys = set(old_vessel_list)
    new_keys = set(vessel_data.keys())

    filename_before_added = None
    same_keys = old_keys & new_keys
    deleted_same_number_vessel_id = old_keys - same_keys
    added_same_number_vessel_id = new_keys - same_keys
    added_same_number_vessel_dict = {}
    deleted_same_number_vessel_list = list(deleted_same_number_vessel_id)
    if len(new_keys) == len(old_keys):
        for key in added_same_number_vessel_id:
            added_same_number_vessel_dict[key] = vessel_data[key]

    if len(new_keys) == len(old_keys):
        filename_after_delete = netxml.delete_netxml(NET_FILENAME_ID, deleted_same_number_vessel_list, ADDED_JUNCTIONS, ADDED_RANGES,
                                               VESSEL2LANE)

        for vessel_id, vessel_info in added_same_number_vessel_dict.items():
            st_x, ed_x = vessel_info.get("tail_x", None), vessel_info.get("head_x", None)
            filename_before_added = netxml.add_netxml_vessel_data_change(filename_after_delete, vessel_id, st_x, ed_x, ADDED_JUNCTIONS, ADDED_RANGES,
                                                VESSEL2LANE)

    add_vessel_dict = {}
    add_vessels_id = new_keys-old_keys
    delete_vessels_id = old_keys - new_keys
    delete_vessel_list = list(delete_vessels_id)
    if len(add_vessels_id) > 0:
        for key in add_vessels_id:
            add_vessel_dict[key] = vessel_data[key]

    if len(delete_vessel_list) > 0:
        filename_before = netxml.delete_netxml(NET_FILENAME_ID, delete_vessel_list, ADDED_JUNCTIONS, ADDED_RANGES, VESSEL2LANE)
    else:
        filename_before_needed_add = NET_FILENAME_ID
    for vessel_id, vessel_info in add_vessel_dict.items():
        st_x, ed_x = vessel_info.get("tail_x", None), vessel_info.get("head_x", None)
        filename_before = netxml.add_netxml_vessel_data_change(filename_before_needed_add, vessel_id, st_x, ed_x,  ADDED_JUNCTIONS, ADDED_RANGES, VESSEL2LANE)

    if filename_before_added:
        filename_before = filename_before_added

    return filename_before


def update_vehicle_tasks(need_update_task:dict, need_add_task:dict, simulation_time, configs):
    global VESSEL2LANE
    global ROUTE_FILENAME
    global APPEND_STOP_LIST
    global VEHICLE_TO_STOP_COORD
    global ADDED_UPDATED_VEHICLE_ID_EDGE
    global TRUCK_UNSUBSCRIBED_SET
    global TRUCK_START_LIST
    converter = routepy.NetworkConverter(configs.BASE_NET_FILE)

    if len(need_add_task)>0:
        logger.info(f"添加集卡车辆及任务")
        for vehicle_id in need_add_task.keys():
            if vehicle_id in VEHICLE_TO_STOP_COORD:
                APPEND_STOP_LIST.append(VEHICLE_TO_STOP_COORD[vehicle_id])
                del VEHICLE_TO_STOP_COORD[vehicle_id]
            TRUCK_START_LIST = routepy.get_all_car_start_coordinates(configs.BASE_NET_FILE, configs.truck_start_type)
            random_truck_start_list = random.choice(TRUCK_START_LIST)
            x, y = random_truck_start_list
            edge_id, lane_pos = converter.convert_coordinates_to_edge(x, y, radius=20)

            if edge_id is None or lane_pos is None or edge_id == "":
                random_truck_start_list = random.choice(TRUCK_START_LIST)
                x, y = random_truck_start_list
                edge_id, lane_pos = converter.convert_coordinates_to_edge(x, y, radius=20)
                if edge_id is None or lane_pos is None or edge_id == "":
                    random_truck_start_list = random.choice(TRUCK_START_LIST)
                    x, y = random_truck_start_list
                    edge_id, lane_pos = converter.convert_coordinates_to_edge(x, y, radius=20)
                    if edge_id is None or lane_pos is None or edge_id == "":
                        edge_id = random.choice(routepy.extract_route_edges_from_file(ROUTE_FILENAME))
                        lane_pos = 1

            ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id] = edge_id
            try:
                current_edge_add = edge_id
                current_pos_add = lane_pos
                temp_route_id = f"temp_route_{vehicle_id}_{int(time.time()*1000)}"
                traci.route.add(temp_route_id, [edge_id])
                traci.vehicle.add(vehID=vehicle_id, routeID=temp_route_id, typeID="myType", departPos=lane_pos, departSpeed=0)
                traci.vehicle.setLength(vehicle_id, 19.0)
                traci.vehicletype.setMaxSpeed("myType", 50.0)
                traci.vehicle.setColor(typeID=vehicle_id, color=(255, 0, 0, 255))
                traci.vehicle.setShapeClass(typeID=vehicle_id, shapeClass="truck")
                traci.vehicle.setSpeed(vehicle_id, 0)
                traci.vehicle.setLaneChangeMode(vehicle_id, 0x5ff)

                if vehicle_task_check(vehicle_id, need_add_task[vehicle_id], simulation_time, configs) == True:
                    set_vehicle_route_and_stop(vehicle_id, need_add_task[vehicle_id], configs, current_edge_update=None, current_pos_update=None, current_edge_add=current_edge_add, current_pos_add=current_pos_add)
                    traci.vehicle.setSpeed(vehicle_id, -1)
                    logger.info(f"新添加车辆 {vehicle_id} 并设置任务完成。")

            except traci.exceptions.TraCIException as e:
                logger.info(f"添加车辆 {vehicle_id} 失败: {e}。尝试直接设置其作业任务。")
                # logger.info(traceback.format_exc())


    if len(need_update_task) > 0:
        logger.info(f"更新集卡任务")
        vehicle_update_positions_coord = {}
        for vehicle_id in need_update_task.keys():

            if vehicle_id in VEHICLE_TO_STOP_COORD:
                APPEND_STOP_LIST.append(VEHICLE_TO_STOP_COORD[vehicle_id])
                del VEHICLE_TO_STOP_COORD[vehicle_id]
            try:
                edge_id = traci.vehicle.getRoadID(vehicle_id)
                lane_pos = traci.vehicle.getLanePosition(vehicle_id)
                route_edges_update_vehicle = traci.vehicle.getRoute(vehicle_id)

                if edge_id is not None and lane_pos is not None and edge_id != "":
                    if ":" in edge_id:
                        prev_edge,next_edge = routepy.find_previous_and_next_edges(route_edges_update_vehicle, edge_id)
                        edge_id = next_edge
                        lane_pos = 1
                        ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id] = edge_id
                        vehicle_update_positions_coord[vehicle_id] = (edge_id, lane_pos)
                    elif ":" not in edge_id:
                        ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id] = edge_id
                        vehicle_update_positions_coord[vehicle_id] = (edge_id, lane_pos)

                elif edge_id is None or lane_pos is None or edge_id == "":
                    random_truck_start_list = random.choice(TRUCK_START_LIST)
                    x, y = random_truck_start_list
                    edge_id, lane_pos = converter.convert_coordinates_to_edge(x, y, radius=20)
                    if edge_id is None or lane_pos is None or edge_id == "":
                        random_truck_start_list = random.choice(TRUCK_START_LIST)
                        x, y = random_truck_start_list
                        edge_id, lane_pos = converter.convert_coordinates_to_edge(x, y, radius=20)
                        if edge_id is None or lane_pos is None or edge_id == "":
                            edge_id = random.choice(routepy.extract_route_edges_from_file(ROUTE_FILENAME))
                            lane_pos = 2

                    ADDED_UPDATED_VEHICLE_ID_EDGE[vehicle_id] = edge_id
                    vehicle_update_positions_coord[vehicle_id] = (edge_id, lane_pos)

            except traci.exceptions.TraCIException as e:
                logger.info(f"获取车辆 {vehicle_id} 的位置失败: {e}")
                # logger.info(traceback.format_exc())
                return False, f"{vehicle_id}车辆任务更新过早，集卡车未在仿真中加载。"
            try:
                traci.vehicle.remove(vehicle_id)
            except traci.exceptions.TraCIException as e:
                logger.info(f"删除车辆 {vehicle_id} 失败: {e}")
                # logger.info(traceback.format_exc())

            edge_id, lane_pos = vehicle_update_positions_coord[vehicle_id]
            current_pos_update = lane_pos
            current_edge_update = edge_id
            try:
                temp_route_id = f"temp_route_{vehicle_id}_{int(time.time()*1000)}"
                traci.route.add(temp_route_id, [edge_id])
                traci.vehicle.add(vehID=vehicle_id, routeID=temp_route_id, typeID="myType", departPos=lane_pos, departSpeed=0)
                traci.vehicle.setLength(vehicle_id, 19.0)
                traci.vehicle.setColor(typeID=vehicle_id, color=(255, 0, 0, 255))
                traci.vehicle.setShapeClass(typeID=vehicle_id, shapeClass="truck")
                traci.vehicle.setSpeed(vehicle_id, 0)
                traci.vehicle.setLaneChangeMode(vehicle_id, 0x5ff)

                if vehicle_task_check(vehicle_id, need_update_task[vehicle_id], simulation_time, configs) == True:
                    set_vehicle_route_and_stop(vehicle_id, need_update_task[vehicle_id], configs, current_edge_update=current_edge_update, current_pos_update=current_pos_update, current_edge_add=None, current_pos_add=None)
                    traci.vehicle.setSpeed(vehicle_id, -1)
                    logger.info(f"集卡{vehicle_id}任务更新完成。")

            except traci.exceptions.TraCIException as e:
                logger.info(f"集卡{vehicle_id} 任务更新失败: {e}。尝试直接设置其作业任务。")


def process_vehicle_data(vehicle_id, simulation_time, SIMULATION_START, vehicle_task_data, TRUCK_INFO4RESTART, all_tk_data, subscription_results):
    global TRUCK_UNSUBSCRIBED_SET
    current_pos = subscription_results[vehicle_id][tc.VAR_POSITION]
    task_id = vehicle_task_data.get("task_id")
    angle = subscription_results[vehicle_id][tc.VAR_ANGLE]
    speed = subscription_results[vehicle_id][tc.VAR_SPEED]
    road_id = subscription_results[vehicle_id][tc.VAR_ROAD_ID]
    lane_index = subscription_results[vehicle_id][tc.VAR_LANE_INDEX]
    depart_lane = subscription_results[vehicle_id][tc.VAR_LANE_ID]
    depart_pos = subscription_results[vehicle_id][tc.VAR_LANEPOSITION]
    truck_stops = traci.vehicle.getStops(vehicle_id)

    vehicle_route_edges = " ".join(traci.vehicle.getRoute(vehicle_id))

    # 程序逻辑：stop_exist 默认为 1
    # 在原有作业点上增加一个停车区停车点
    # 获取最后一个停车点，并判断车辆是否到达
    # 到达后，发出最后一次坐标，然后不再发送坐标
    stop_num = len(traci.vehicle.getStops(vehicle_id))
    stop_exist = 1

    if stop_num == 1:
        stops = traci.vehicle.getStops(vehicle_id)
        stop = stops[0]
        current_lane_pos = subscription_results[vehicle_id][tc.VAR_LANEPOSITION]
        current_lane = subscription_results[vehicle_id][tc.VAR_LANE_ID]
        TOLERANCE = 1.5
        if (current_lane == stop.lane and stop.startPos - TOLERANCE <= current_lane_pos <= stop.endPos + TOLERANCE and traci.vehicle.isStopped(vehicle_id)):
            stop_exist = 0

    vehicle_tk_data = {
        "truck_id": vehicle_id,
        "task_id": task_id,
        "simulation_start": SIMULATION_START,
        "simulation_time": simulation_time,
        "current_pos": current_pos,
        "angle": (angle - 90) % 360,
        "stop_exist": stop_exist
    }

    all_tk_data[vehicle_id] = vehicle_tk_data

    TRUCK_INFO4RESTART[vehicle_id] = {
        "depart_lane": depart_lane,
        "depart_pos": depart_pos,
        "depart_speed": speed,
        "vehicle_route_edges": vehicle_route_edges,
        "truck_stops": truck_stops,
        "route_edges": road_id
    }

    if stop_exist == 0:
        traci.vehicle.unsubscribe(vehicle_id)
        TRUCK_UNSUBSCRIBED_SET.add(vehicle_id)
        logger.info(f"{vehicle_id}作业完成，状态是{stop_exist}，坐标为：{current_pos}，已取消订阅。")


def run_netconvert(net_filename, configs):
    """
    使用 netconvert 运行并保存路网文件。
    """
    netconvert_command = [
        configs.netConvert,
        "--sumo-net-file",
        net_filename,
        "-o",
        net_filename,
        "--no-warnings", "true"
    ]
    subprocess.run(netconvert_command, check=True)


def vehicle_reset_route_stop_after_restart_simulation(current_edge, vehicle_id, configs, depart_pos):
    global APPEND_STOP_LIST, VEHICLE_TO_STOP_COORD, TRUCK_SUBSCRIBED_SET, TRUCK_UNSUBSCRIBED_SET
    temp_route_id = f"temp_route_{vehicle_id}_{int(time.time() * 1000)}"
    traci.route.add(temp_route_id, [current_edge])
    traci.vehicle.add(vehID=vehicle_id, routeID=temp_route_id, typeID="myType", departPos=depart_pos, departSpeed=0)
    traci.vehicle.setLength(vehicle_id, 19.0)
    traci.vehicletype.setMaxSpeed("myType", 50.0)
    traci.vehicletype.setAccel("myType", 5.0)
    traci.vehicletype.setDecel("myType", 9.0)
    traci.vehicletype.setTau("myType", 1.0)
    traci.vehicle.setColor(typeID=vehicle_id, color=(255, 0, 0, 255))
    traci.vehicle.setShapeClass(typeID=vehicle_id, shapeClass="truck")
    traci.vehicle.setSpeed(vehicle_id, 0)
    traci.vehicle.setLaneChangeMode(vehicle_id, 0x5ff)

    if vehicle_id in VEHICLE_TO_STOP_COORD:
        stop_edge = VEHICLE_TO_STOP_COORD[vehicle_id]
        rest_stop_edge = stop_edge['lane'][:-2]
    else:
        random_stop_temp = min(APPEND_STOP_LIST, key=lambda x: x['priority'])
        APPEND_STOP_LIST.remove(random_stop_temp)
        VEHICLE_TO_STOP_COORD[vehicle_id] = random_stop_temp
        rest_stop_edge = random_stop_temp['lane'][:-2]
    lane_index = 0
    lane_pos = 3
    rest_route = traci.simulation.findRoute(current_edge, rest_stop_edge)
    traci.vehicle.setRoute(vehicle_id, rest_route.edges)
    if lane_pos < depart_pos:
        lane_pos = depart_pos
    try:
        traci.vehicle.setStop(vehID=vehicle_id, edgeID=rest_stop_edge, laneIndex=lane_index, pos=lane_pos,
                              duration=configs.TRUCK_STOP_TIME)
    except Exception:
        lane_pos =2
        traci.vehicle.setStop(vehID=vehicle_id, edgeID=rest_stop_edge, laneIndex=lane_index, pos=lane_pos,
                              duration=configs.TRUCK_STOP_TIME)
    traci.vehicle.setSpeed(vehicle_id, -1)

    if vehicle_id not in TRUCK_UNSUBSCRIBED_SET:
        traci.vehicle.subscribe(vehicle_id, [
            tc.VAR_POSITION,
            tc.VAR_ANGLE,
            tc.VAR_SPEED,
            tc.VAR_ROAD_ID,
            tc.VAR_LANE_INDEX,
            tc.VAR_LANE_ID,
            tc.VAR_LANEPOSITION
        ])
        TRUCK_SUBSCRIBED_SET.add(vehicle_id)


def vehicle_load_after_restart_simulation(configs, TRUCK_INFO4RESTART, task_data, vessel_data_LAST):
    global TRUCK_SUBSCRIBED_SET, TRUCK_UNSUBSCRIBED_SET,VESSEL2LANE
    if vessel_data_LAST is None or len(vessel_data_LAST) ==0:
        json_vessel_data = configs.redis_conn.get("vessel_data")
        vessel_data = json.loads(json_vessel_data)
        vessel_data_LAST = vessel_data
    vehicle_ids = task_data.keys()

    current_vessel_id_list = []
    for vessel_id in vessel_data_LAST:
        current_vessel_id_list.append(VESSEL2LANE[vessel_id]["s1_1Ts1_2"]["edge_id"])
        current_vessel_id_list.append(VESSEL2LANE[vessel_id]["s2_1Ts2_2"]["edge_id"])

    for vehicle_id in vehicle_ids:
        if vehicle_id not in TRUCK_INFO4RESTART:
            logger.warning(f"警告：vehicle_id {vehicle_id} 在 TRUCK_INFO4RESTART 中不存在，跳过")
            continue
        truck_info = TRUCK_INFO4RESTART[vehicle_id]
        vehicle_route_edges = truck_info["vehicle_route_edges"]
        road_id = truck_info["route_edges"]
        depart_pos = truck_info["depart_pos"]
        route_edges_list = vehicle_route_edges.split(" ")
        truck_stops = truck_info["truck_stops"]
        if depart_pos is None:
            depart_pos = 2

        if ":" in road_id:
            prev_edge, next_edge = routepy.find_previous_and_next_edges(route_edges_list, road_id)
            road_id = next_edge
            depart_pos = 1

            try:
                current_index = route_edges_list.index(road_id)
            except ValueError:
                continue

            stop_indices = []
            for stop in truck_stops:
                lane_id = stop.lane
                edge_id = lane_id.split("_")[0]
                try:
                    stop_index = route_edges_list.index(edge_id)
                    stop_indices.append(stop_index)
                except ValueError:
                    continue

            if current_index in stop_indices:
                current_edge_restart = route_edges_list[current_index]
                current_position_restart = depart_pos
            else:
                current_edge_restart = route_edges_list[current_index]
                current_position_restart = depart_pos

            remaining_route = route_edges_list[current_index:]
            if not any(edge in current_vessel_id_list for edge in remaining_route):
                route_length = len(route_edges_list)
                if current_index in stop_indices:
                    target_index_stop = current_index + 2
                    if target_index_stop < route_length:
                        current_edge = route_edges_list[target_index_stop]
                    else:
                        current_edge = route_edges_list[-1]
                    logger.info(f"{vehicle_id} 的路径中不存在作业点，将驶去停车区。")
                    vehicle_reset_route_stop_after_restart_simulation(current_edge, vehicle_id, configs, depart_pos)
                continue

            temp_route_id = f"route_{vehicle_id}_{int(traci.simulation.getTime())}"
            traci.route.add(temp_route_id, remaining_route)

            # 添加车辆
            traci.vehicle.add(vehID=vehicle_id, routeID=temp_route_id, typeID="myType", departPos=depart_pos, departSpeed="random")
            traci.vehicle.setLength(vehicle_id, 19.0)
            traci.vehicletype.setMaxSpeed("myType", 50.0)
            traci.vehicletype.setAccel("myType", 5.0)
            traci.vehicletype.setDecel("myType", 9.0)
            traci.vehicletype.setTau("myType", 1.0)
            traci.vehicle.setColor(typeID=vehicle_id, color=(255, 0, 0, 255))
            traci.vehicle.setShapeClass(typeID=vehicle_id, shapeClass="truck")
            traci.vehicle.setLaneChangeMode(vehicle_id, 0x5ff)

            # 设置停车点
            for stop in truck_stops:
                lane_id = stop.lane
                lane_index = int(lane_id.rsplit("_", 1)[-1])
                edge_id = lane_id.split("_")[0]
                try:
                    stop_index = route_edges_list.index(edge_id)
                    if stop_index > current_index:
                        lane_pos = stop.startPos
                        try:
                            if current_edge_restart == edge_id and current_position_restart > lane_pos:
                                lane_pos = current_position_restart + 0.5
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index, pos=lane_pos, duration=stop.duration)
                            else:
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index, pos=lane_pos, duration=stop.duration)
                        except Exception:
                            lane_pos = 2
                            if current_edge_restart == edge_id and current_position_restart > lane_pos:
                                lane_pos = current_position_restart + 0.5
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index, pos=lane_pos, duration=stop.duration)
                            else:
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index, pos=lane_pos, duration=stop.duration)
                except ValueError:
                    continue

        elif ":" not in road_id:
            try:
                current_index = route_edges_list.index(road_id)
            except ValueError:
                continue

            stop_indices = []
            for stop in truck_stops:
                lane_id = stop.lane
                edge_id = lane_id.split("_")[0]
                try:
                    stop_index = route_edges_list.index(edge_id)
                    stop_indices.append(stop_index)
                except ValueError:
                    continue

            if current_index in stop_indices:
                current_edge_restart = route_edges_list[current_index]
                current_position_restart = depart_pos
            else:
                current_edge_restart = route_edges_list[current_index]
                current_position_restart = depart_pos

            remaining_route = route_edges_list[current_index:]
            if not any(edge in current_vessel_id_list for edge in remaining_route):
                route_length = len(route_edges_list)
                if current_index in stop_indices:
                    target_index_stop = current_index + 2
                    if target_index_stop < route_length:
                        current_edge = route_edges_list[target_index_stop]
                    else:
                        current_edge = route_edges_list[-1]
                    logger.info(f"{vehicle_id} 的路径中不存在作业点，将驶去停车区。")
                    vehicle_reset_route_stop_after_restart_simulation(current_edge, vehicle_id, configs, depart_pos)
                continue

            temp_route_id = f"route_{vehicle_id}_{int(traci.simulation.getTime())}"
            traci.route.add(temp_route_id, remaining_route)

            # 添加车辆
            traci.vehicle.add(vehID=vehicle_id, routeID=temp_route_id, typeID="myType", departPos=depart_pos, departSpeed="random")
            traci.vehicle.setLength(vehicle_id, 19.0)
            traci.vehicletype.setMaxSpeed("myType", 50.0)
            traci.vehicletype.setAccel("myType", 5.0)
            traci.vehicletype.setDecel("myType", 9.0)
            traci.vehicletype.setTau("myType", 1.0)
            traci.vehicle.setColor(typeID=vehicle_id, color=(255, 0, 0, 255))
            traci.vehicle.setShapeClass(typeID=vehicle_id, shapeClass="truck")
            traci.vehicle.setLaneChangeMode(vehicle_id, 0x5ff)

            # 设置停车点
            for stop in truck_stops:
                lane_id = stop.lane
                lane_index = int(lane_id.rsplit("_", 1)[-1])
                edge_id = lane_id.split("_")[0]
                try:
                    stop_index = route_edges_list.index(edge_id)
                    if stop_index > current_index:
                        lane_pos = stop.startPos
                        try:
                            if current_edge_restart == edge_id and current_position_restart > lane_pos:
                                lane_pos = current_position_restart + 0.5
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index,
                                                      pos=lane_pos, duration=stop.duration)
                            else:
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index,
                                                      pos=lane_pos, duration=stop.duration)
                        except Exception:
                            lane_pos = 2
                            if current_edge_restart == edge_id and current_position_restart > lane_pos:
                                lane_pos = current_position_restart + 0.5
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index,
                                                      pos=lane_pos, duration=stop.duration)
                            else:
                                traci.vehicle.setStop(vehID=vehicle_id, edgeID=edge_id, laneIndex=lane_index,
                                                      pos=lane_pos, duration=stop.duration)
                except ValueError:
                    continue

        if vehicle_id not in TRUCK_UNSUBSCRIBED_SET:
            traci.vehicle.subscribe(vehicle_id, [
                tc.VAR_POSITION,
                tc.VAR_ANGLE,
                tc.VAR_SPEED,
                tc.VAR_ROAD_ID,
                tc.VAR_LANE_INDEX,
                tc.VAR_LANE_ID,
                tc.VAR_LANEPOSITION
            ])
            TRUCK_SUBSCRIBED_SET.add(vehicle_id)


def simulation_step(SIMULATION_START, NET_FILENAME_BACKUPS, configs):
    global VESSEL2LANE, TRUCK_UNSUBSCRIBED_SET, TRUCK_START_LIST, ADDED_UPDATED_VEHICLE_ID_EDGE, TRUCK_SUBSCRIBED_SET, ROUTE_FILENAME, APPEND_STOP_LIST, ADDED_RANGES, ADDED_JUNCTIONS, WARNED_VEHICLE_SET
    logger.info("开始码头集卡装卸作业路径仿真...")
    i = 0
    j = 0
    LAST_task_data = ''
    LAST_vessel_data = ''
    while True:
        try:
            start_time = datetime.now()
            target_time = start_time + timedelta(seconds=1)

            traci.simulationStep()
            simulation_time = traci.simulation.getTime()
            TRUCK_INFO4RESTART = {}
            all_tk_data = {}
            vessel_data_LAST = {}
            json_trucks = configs.redis_conn.hgetall("task_data")
            task_data = {key.decode(): json.loads(value) for key, value in json_trucks.items()}

            subscription_results = traci.vehicle.getAllSubscriptionResults()

            for vehicle_id in subscription_results.keys():
                vehicle_task_data = task_data.get(vehicle_id)
                omitted_condition1 = vehicle_id in TRUCK_UNSUBSCRIBED_SET
                if omitted_condition1:
                    continue
                if vehicle_task_data is None:
                    if vehicle_id not in WARNED_VEHICLE_SET:
                        WARNED_VEHICLE_SET.add(vehicle_id)
                        logger.warning(f"Vehicle {vehicle_id} 不在 task_data 中,数据异常。")
                    continue

                process_vehicle_data(vehicle_id, simulation_time, SIMULATION_START, vehicle_task_data, TRUCK_INFO4RESTART, all_tk_data, subscription_results)

            tk_data = {vehicle_id: json.dumps(tk_data) for vehicle_id, tk_data in all_tk_data.items()}
            while True:
                current_time = datetime.now()
                if (current_time - target_time).total_seconds() >= -0.005 or current_time >= target_time:
                    break
                time.sleep(0.0001)

            redis_task_data_manager(tk_data, configs, vehicle_id=None)

            json_trucks = configs.redis_conn.hgetall("task_data")
            task_data = {key.decode(): json.loads(value) for key, value in json_trucks.items()}

            if i == 0:
                LAST_task_data = copy.deepcopy(task_data)
                logger.critical(f"标志1：初始化集卡任务！")
                for vehicle_id, task_info in task_data.items():
                    if vehicle_task_check(vehicle_id, task_data[vehicle_id], simulation_time, configs) == True:
                        set_vehicle_route_and_stop(vehicle_id, task_info, configs, current_edge_update=None, current_pos_update=None, current_edge_add=None, current_pos_add=None)
                i += 1
            else:
                cur_sumo_tk_id = traci.vehicle.getLoadedIDList()

                need_add_task = {}
                need_update_task = {}
                for tk_id,task in task_data.items():
                    if tk_id not in cur_sumo_tk_id:
                        need_add_task[tk_id] = task
                    else:
                        old_task = LAST_task_data.get(tk_id)
                        if old_task is None:
                            need_update_task[tk_id] = task
                        else:
                            if task['task_id'] != old_task['task_id']:
                                need_update_task[tk_id] = task

                if len(need_add_task) > 0 or len(need_update_task) > 0:
                    update_vehicle_tasks(need_update_task, need_add_task, simulation_time, configs)
                    LAST_task_data = copy.deepcopy(task_data)

            json_vessel_data = configs.redis_conn.get("vessel_data")
            vessel_data = json.loads(json_vessel_data)

            if j == 0:
                LAST_vessel_data = copy.deepcopy(vessel_data)
                j += 1
                continue
            else:
                old_keys = set(LAST_vessel_data.keys())
                new_keys = set(vessel_data.keys())
                vessel_data_LAST = vessel_data
                if (len(new_keys) > len(old_keys)) or (len(old_keys) == len(new_keys) and new_keys != old_keys):
                    NET_FILENAME_BEFORE_UPDATE = NET_FILENAME_BACKUPS
                    TRUCK_SUBSCRIBED_SET.clear()
                    ADDED_RANGES.clear()
                    ADDED_JUNCTIONS.clear()
                    VESSEL2LANE.clear()
                    WARNED_VEHICLE_SET.clear()
                    try:
                        traci.close()
                        NET_FILENAME_UPDATE, NET_FILENAME_BACKUPS = initialize_netxml(vessel_data, configs)
                        NET_FILENAME = NET_FILENAME_UPDATE
                        logger.warning(f'添加船舶并升级路网为{os.path.basename(NET_FILENAME_UPDATE)}')

                        try:
                            run_netconvert(NET_FILENAME, configs)
                            print(f"路网文件{os.path.basename(NET_FILENAME_UPDATE)}处理成功，继续使用更新后的文件。")
                        except subprocess.CalledProcessError:
                            vessel_info = "; ".join(
                                f"{vessel_id}, \"head_x\": {data['head_x']}, \"tail_x\": {data['tail_x']}"
                                for vessel_id, data in vessel_data.items()
                            )
                            logger.critical(f"处理路网文件{os.path.basename(NET_FILENAME_UPDATE)}时发生错误，使用旧路网文件。"
                                            f"当前任务批次的船舶信息如下：{vessel_info}")
                            NET_FILENAME = NET_FILENAME_BEFORE_UPDATE

                        sumoCmd = [
                            configs.sumoBinary,
                            "--net-file", NET_FILENAME,
                            "--route-files", "type.rou.xml",
                            "--gui-settings-file", configs.gui_settings_file,
                            "--time-to-teleport", "15",
                            "--step-length", "1",
                            "--route-steps", "0",
                            "--no-warnings", "true"
                        ]

                        traci.start(sumoCmd)
                        SIMULATION_START = datetime.now().strftime("%Y%m%d-%H:%M:%S")
                        json_trucks = configs.redis_conn.hgetall("task_data")
                        task_data = {key.decode(): json.loads(value) for key, value in json_trucks.items()}

                        logger.info(f"重新加载上次仿真中集卡车的位置。")
                        vehicle_load_after_restart_simulation(configs, TRUCK_INFO4RESTART, task_data, vessel_data_LAST)

                        logger.critical(f"已重启仿真会话，使用新路网文件: {os.path.basename(NET_FILENAME)}")
                        LAST_vessel_data = copy.deepcopy(vessel_data)
                    except Exception as e:
                        logger.info(f"启动仿真时发生错误: {e}")
                        # logger.info(traceback.format_exc())

                elif len(old_keys) > len(new_keys):
                    ADDED_RANGES.clear()
                    ADDED_JUNCTIONS.clear()
                    VESSEL2LANE.clear()
                    try:
                        NET_FILENAME_UPDATE, NET_FILENAME_BACKUPS = initialize_netxml(vessel_data, configs)
                        logger.warning(f'删除船舶并升级路网为{os.path.basename(NET_FILENAME_UPDATE)}')
                    except subprocess.CalledProcessError:
                        logger.info(f"处理路网文件{os.path.basename(NET_FILENAME_UPDATE)}时发生错误，使用旧路网文件。")
                        logger.info("当前任务批次的船舶信息如下：")
                        vessel_info = "; ".join(
                            f"{vessel_id}, \"head_x\": {data['head_x']}, \"tail_x\": {data['tail_x']}"
                            for vessel_id, data in vessel_data.items()
                        )
                        logger.info(f"{{{vessel_info}}}")
                    LAST_vessel_data = copy.deepcopy(vessel_data)

        except Exception as e:
            logger.info(f"发生错误{e}，重置仿真会话。")
            VESSEL2LANE.clear()
            ADDED_RANGES.clear()
            ADDED_JUNCTIONS.clear()
            ADDED_UPDATED_VEHICLE_ID_EDGE.clear()
            TRUCK_UNSUBSCRIBED_SET.clear()
            TRUCK_SUBSCRIBED_SET.clear()
            WARNED_VEHICLE_SET.clear()
            ROUTE_FILENAME = ''
            if traci.isLoaded():
                traci.close()
            json_vessel_data = configs.redis_conn.get("vessel_data")
            vessel_data = json.loads(json_vessel_data)
            task_data_json = configs.redis_conn.hgetall("task_data")
            task_data = {key.decode(): json.loads(value) for key, value in task_data_json.items()}

            if len(vessel_data) == 0 or len(task_data) == 0:
                logger.warning("没有货船数据，请等待...")
                time.sleep(10)
                continue

            NET_FILENAME, NET_FILENAME_BACKUPS = initialize_netxml(vessel_data, configs)

            try:
                run_netconvert(NET_FILENAME, configs)
                print(f"路网文件{os.path.basename(NET_FILENAME)}处理成功，继续使用更新后的文件。")
            except subprocess.CalledProcessError:
                logger.info(f"处理路网文件{os.path.basename(NET_FILENAME)}时发生错误。")
                logger.info("当前任务批次的船舶信息如下：")
                vessel_info = "; ".join(
                    f"{vessel_id}, \"head_x\": {data['head_x']}, \"tail_x\": {data['tail_x']}"
                    for vessel_id, data in vessel_data.items()
                )
                logger.info(f"{{{vessel_info}}}")

            sumoCmd = [
                configs.sumoBinary,
                "--net-file", NET_FILENAME,
                "--route-files", "type.rou.xml",
                "--gui-settings-file", configs.gui_settings_file,
                "--time-to-teleport", "15",
                "--step-length", "1",
                "--route-steps", "0",
                "--no-warnings", "true",
                "--ignore-route-errors", "true"
            ]
            traci.start(sumoCmd)
            SIMULATION_START = datetime.now().strftime("%Y%m%d-%H:%M:%S")

            logger.info(f"重新加载上次仿真中集卡车的位置。")
            vehicle_load_after_restart_simulation(configs, TRUCK_INFO4RESTART, task_data, vessel_data_LAST)


def run(configs):
    global ROUTE_FILENAME, APPEND_STOP_LIST, TRUCK_START_LIST,ADDED_UPDATED_VEHICLE_ID_EDGE,TRUCK_SUBSCRIBED_SET,TRUCK_UNSUBSCRIBED_SET
    ADDED_UPDATED_VEHICLE_ID_EDGE.clear()
    TRUCK_UNSUBSCRIBED_SET.clear()
    TRUCK_SUBSCRIBED_SET.clear()
    WARNED_VEHICLE_SET.clear()
    ROUTE_FILENAME = ''

    json_vessel_data = configs.redis_conn.get("vessel_data")
    vessel_data = json.loads(json_vessel_data)
    task_data_json = configs.redis_conn.hgetall("task_data")
    task_data = {key.decode(): json.loads(value) for key, value in task_data_json.items()}

    if len(vessel_data) == 0 or len(task_data) == 0:
        logger.warning("没有货船数据，请等待...")
        time.sleep(10)
        return

    NET_FILENAME, NET_FILENAME_BACKUPS = initialize_netxml(vessel_data, configs)

    logger.info(f'首次({len(vessel_data)})条货船、({len(task_data)})辆集卡的路网文件初始化成功：{NET_FILENAME}。')
    run_netconvert(NET_FILENAME, configs)   # 返回Success

    TRUCK_START_LIST = routepy.get_all_car_start_coordinates(configs.BASE_NET_FILE, configs.truck_start_type)
    APPEND_STOP_LIST = routepy.get_all_f_area_rest_stop_coordinates(configs.BASE_NET_FILE, configs.truck_stop_type_f,
                                                                    configs.TRUCK_STOP_TIME)
    buffer_rest_stop_coordinates = routepy.get_Buffer_area_rest_stop_coordinates(configs.BASE_NET_FILE,
                                                                                 configs.truck_buffer_stop_type,
                                                                                 configs.TRUCK_STOP_TIME)
    APPEND_STOP_LIST.extend(buffer_rest_stop_coordinates)
    rest_stop_coordinates = routepy.get_all_rest_stop_coordinates(configs.BASE_NET_FILE,
                                                                  configs.truck_stop_type,
                                                                  configs.TRUCK_STOP_TIME)
    APPEND_STOP_LIST.extend(rest_stop_coordinates) #559个停车点

    if task_data:
        ROUTE_FILENAME = routepy.initialize_rouxml(configs.BASE_NET_FILE, task_data, TRUCK_START_LIST,
                                                   configs.BATCH_SIZE, configs.INTERVAL)
    else:
        logger.info("集卡任务数据为空，无法生成集卡初始位置信息，请检查数据链路！！！！")
        return

    sumoCmd = [
        configs.sumoBinary,
        "--net-file", NET_FILENAME,
        "--route-files", ROUTE_FILENAME,
        "--gui-settings-file", configs.gui_settings_file,
        "--time-to-teleport", "15",
        "--step-length", "1",
        "--route-steps", "0",
        "--no-warnings", "true",
        "--ignore-route-errors", "true"
    ]
    traci.start(sumoCmd)

    simulation_step(configs.SIMULATION_START, NET_FILENAME_BACKUPS, configs)
